extern crate rand;
use std::{
    io::{self},
};
use std::process::Command;

use rand::Rng;
fn main() -> std::io::Result<()> {
    let mut input_len = String::new();
    println!("guess number length (4~6):");
    io::stdin().read_line(&mut input_len)?;
    let input_len_val = match input_len.trim().parse::<u8>() {
        Ok(num)=> match num >=4 && num <=6{
            true => num,
            false => 4,
        },
        Err(_)=>{
            println!("number length must be a number!");
            4
        },
    };


    let target_val = generate(&input_len_val);
    let mut input = String::new();
    let mut record: Vec<(String, String)> = Vec::new();
    loop {
        if record.len() > 0 {
            println!("try another:");
        } else {
            println!("Please input a number with length {}:",input_len_val);
        }
        if !input.is_empty() {
            input.clear();
        }
        io::stdin().read_line(&mut input)?;
        if input.trim().eq("?") {
            for (i, (guess, res)) in record.clone().into_iter().enumerate() {
                println!("{:?}. guess {:?} result {:?} !", i + 1, guess, res);
            }
            continue;
        }
        if input.trim().eq("!") {
            println!("real number is {:?} !",target_val);
            continue;
        }
        match input.trim_end().parse::<u32>() {
            Ok(num) => num,
            Err(_) => {
                println!("not a number!");
                continue;
            }
        };

        let (res, res_str) = cmp_str_num(&input, &target_val.to_string(),&input_len_val);
        if res {
            println!("congratulations! you guess the number, use {} steps:",record.len());
            for (i, (guess, res)) in record.clone().into_iter().enumerate() {
                println!("{:?}. guess {:?} result {:?} !", i + 1, guess, res);
            }
            break;
        } else {
            println!("you get a result: {}", res_str);
        }

        record.push((
            input.trim().to_string().clone(),
            res_str.trim().to_string().clone(),
        ));
    }
    let _ = Command::new("cmd.exe").arg("/c").arg("pause").status();
    Ok(())
}

fn generate(len: &u8) -> String {
    let mut v: Vec<u32> = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    let mut index: u32 = 0;
    let mut result: Vec<u32> = Vec::new();
    let len = len.clone() as u32;
    loop {
        if index == len {
            break;
        }
        let i = rand::thread_rng().gen_range(0..v.len());
        result.push(v.remove(i));
        index += 1;
    }
    match result.into_iter().reduce(|x, y| y + x * 10) {
        Some(num) => match num > 10 * len {
            false => format!("0{}", num.to_string()),
            true => num.to_string(),
        },
        _ => "".to_string(),
    }
}

fn cmp_str_num(str_src: &String, str_tag: &String, size:&u8) -> (bool, String) {
    let chars_src = str_src.chars();
    let mut A = 0;
    let mut B = 0;
    let size = size.clone() as u32;
    for (i, val_src) in chars_src.into_iter().enumerate() {
        let chars_tag = str_tag.chars();
        for (j, val_tag) in chars_tag.into_iter().enumerate() {
            if val_src == val_tag && i == j {
                A += 1;
                break;
            } else if val_src == val_tag {
                B += 1;
                break;
            }
        }
    }
    (A == size, String::from(format!("{}A{}B", A, B)))
}

#[test]
fn test_generate() {
    let res = generate(&4);
    assert_ne!("1234", res)
}

#[test]
fn test_cmp_str_num() {
    let src = "1234".to_string();
    let tgt = "1345".to_string();
    let (res, res_str) = cmp_str_num(&src, &tgt,&4);
    assert_eq!(res, false);
    assert_eq!(res_str, "1A2B");
}
